home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HPAVC
/
HPAVC CD-ROM.iso
/
ARASAN_S.ZIP
/
BOARD.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-14
|
25KB
|
881 lines
// Copyright 1994 by Jon Dart. All Rights Reserved.
#include "board.h"
#include "rmove.h"
#include "constant.h"
#include "bhash.h"
#include "util.h"
#include "bearing.h"
#include <strstream.h>
#include <ctype.h>
enum {BoardSize = 64};
void Board::set_secondary_vars()
{
int index[2], i;
index[Black] = index[White] = 0;
for (i=0;i<16;i++)
my_PiecePos[i][White] = my_PiecePos[i][Black] = Square::Invalid();
my_Material[White].clear();
my_Material[Black].clear();
for (i=0;i<8;i++)
{
my_PFileCount[i][White] = my_PFileCount[i][Black] = 0;
my_RFileCount[i][White] = my_RFileCount[i][Black] = 0;
}
for (i=0;i<BoardSize;i++)
{
Square sq(i);
if (!my_Contents[sq].IsEmpty())
{
const Piece piece = my_Contents[sq];
const ColorType color = piece.Color();
my_PiecePos[index[color]++][color] = sq;
my_Material[color].add_piece(piece.Type());
if (piece.Type() == Piece::King)
my_KingPos[color] = sq;
if (piece.Type() == Piece::Pawn)
{
my_PFileCount[sq.File()-1][color]++;
}
else if (piece.Type() == Piece::Rook)
{
my_RFileCount[sq.File()-1][color]++;
}
}
}
my_HashCode = Board_Hash::HashCode(*this);
my_attacks.clear();
my_attacks.compute_attacks(*this,White);
my_attacks.compute_attacks(*this,Black);
}
void Board::Reset()
{
static Piece::PieceType pieces[] =
{
Piece::Rook,
Piece::Knight,
Piece::Bishop,
Piece::Queen,
Piece::King,
Piece::Bishop,
Piece::Knight,
Piece::Rook
};
my_Side = White;
for (int i=0;i<BoardSize;i++)
{
const Square sq(i);
if (sq.Rank(White) == 1)
my_Contents[sq] = Piece( pieces[sq.File()-1], White );
else if (sq.Rank(Black) == 1)
my_Contents[sq] = Piece( pieces[sq.File()-1], Black );
else if (sq.Rank(White) == 2)
my_Contents[sq] = Piece( Piece::Pawn, White);
else if (sq.Rank(Black) == 2)
my_Contents[sq] = Piece( Piece::Pawn, Black);
else
my_Contents[sq] = Piece::EmptyPiece();
}
my_EnPassantSq[White] = my_EnPassantSq[Black] = Square::Invalid();
my_CastleStatus[White] = my_CastleStatus[Black] = CanCastleEitherSide;
set_secondary_vars();
}
Board::Board()
{
Reset();
}
#ifdef RANGE_CHECK
const Piece &Board::operator[]( const Square sq ) const
{
assert(sq.OnBoard());
return my_Contents[sq];
}
#endif
Piece Board::GetPiece( const Square start, const int dir, Square &dest ) const
// returns 1st piece in the given direction from "start". Also
// sets "dest", where it is
{
dest = start;
do {
dest = dest + dir;
} while (!dest.OnEdge() && my_Contents[dest].IsEmpty());
return my_Contents[dest];
}
Board::~Board()
{
}
void Board::UpdateCastleStatus( const ColorType side,
const Square sq )
// after a move of or capture of the rook on 'sq', update castle status
// for 'side'
{
CastleType cs = CastleStatus(side);
if ((cs != CastledKSide) && (cs != CastledQSide))
{
if (sq.File() == 1) // Queen Rook moved or captured
{
if (cs == CanCastleEitherSide)
my_CastleStatus[side] = CanCastleKSide;
else if (cs == CanCastleQSide)
my_CastleStatus[side] = CantCastleEitherSide;
}
else if (sq.File() == 8) // King Rook moved or captured
{
if (cs == CanCastleEitherSide)
my_CastleStatus[side] = CanCastleQSide;
else if (cs == CanCastleKSide)
my_CastleStatus[side] = CantCastleEitherSide;
}
}
}
int Board::Direction( const Square &sq1, const Square &sq2 ) const
{
if (sq2 == sq1)
return 0;
int offset;
int abs = (sq2 > sq1) ? (int)sq2 - (int)sq1 : (int)sq1 - (int)sq2;
if (sq1.File() == sq2.File())
offset = RankIncr;
else if (sq1.Rank(White) == sq2.Rank(White))
offset = 1;
else if (sq1.Color() == sq2.Color())
{
if (abs % (RankIncr+1) == 0)
offset = RankIncr+1;
else if (abs % (RankIncr-1) == 0)
offset = RankIncr-1;
else
return 0;
}
else
return 0;
return (sq2 > sq1) ? offset : -offset;
}
int Board::Between( const Square &sq1, const Square &sq2,
Square *squares) const
{
int offset = Direction(sq1,sq2);
Square sq(sq1);
int n = 0;
for (;;)
{
sq += offset;
if (sq == sq2)
break;
squares[n++] = sq;
}
return n;
}
Boolean Board::Clear( const Square &sq1, const Square &sq2 ) const
{
Square BtwnSquares[9];
int m = Between(sq1,sq2,BtwnSquares);
Boolean is_clear = True;
if (m == 0)
return True;
else
{
int k = 0;
do
{
is_clear = my_Contents[BtwnSquares[k++]].IsEmpty();
} while (is_clear && (k<m));
}
return is_clear;
}
void Board::MakeMove( const ExtendedMove &move )
{
int i,j;
const ColorType oside = OppositeSide();
const ColorType side = Side();
Square kp,target;
if (move.IsNull())
{
my_Side = oside;
Board_Hash::UpdateHash( *this, move, my_HashCode, my_HashCode );
return;
}
else
Board_Hash::UpdateHash( *this, move, my_HashCode, my_HashCode );
if (move.Special() == ExtendedMove::KCastle)
{
const Square kp = KingPos(side);
my_attacks.remove_attacks(*this,kp,side);
my_attacks.remove_attacks(*this,kp+3,side);
i = 0;
while (i < 16 && PiecePos(side,i) != kp)
i++;
assert(i<16);
my_KingPos[side] = kp + 2;
my_PiecePos[i][side] = my_KingPos[side];
my_CastleStatus[side] = CastledKSide;
// find old square of rook
Square oldrooksq = kp + 3;
Square newrooksq = kp + 1;
i = 0;
while (i < 16 && PiecePos(side,i) != oldrooksq)
i++;
assert(i<16);
my_PiecePos[i][side] = newrooksq; // update rook position
my_Contents[kp] = my_Contents[oldrooksq] = Piece::EmptyPiece();
my_Contents[newrooksq] = Piece(Piece::Rook,side);
my_Contents[kp+2] = Piece(Piece::King,side);
my_RFileCount[oldrooksq.File()-1][side]--;
my_RFileCount[newrooksq.File()-1][side]++;
my_attacks.add_attacks(*this,kp+1,side);
my_attacks.add_attacks(*this,kp+2,side);
my_attacks.add_discovered_attacks(*this,
kp,kp+1);
}
else if (move.Special() == ExtendedMove::QCastle)
{
const Square kp = KingPos(side);
my_attacks.remove_attacks(*this,kp,side);
my_attacks.remove_attacks(*this,kp-4,side);
i = 0;
while (i < 16 && PiecePos(side,i) != kp)
i++;
assert(i < 16);
my_KingPos[side] = kp - 2;
my_PiecePos[i][side] = my_KingPos[side];
my_CastleStatus[side] = CastledQSide;
// find old square of rook
Square oldrooksq = kp - 4;
Square newrooksq = kp - 1;
i = 0;
while (i < 16 && PiecePos(side,i) != oldrooksq)
i++;
assert(i<16);
my_PiecePos[i][side] = newrooksq; // update rook position
my_Contents[kp] = my_Contents[oldrooksq] = Piece::EmptyPiece();
my_Contents[newrooksq] = Piece(Piece::Rook,side);
my_Contents[kp-2] = Piece(Piece::King,side);
my_RFileCount[oldrooksq.File()-1][side]--;
my_RFileCount[newrooksq.File()-1][side]++;
my_attacks.add_attacks(*this,kp-1,side);
my_attacks.add_attacks(*this,kp-2,side);
my_attacks.add_discovered_attacks(*this,
kp,kp-1);
}
else // not castling
{
Square target;
if (move.Special() == ExtendedMove::EnPassant)
target = my_EnPassantSq[oside];
else
target = move.DestSquare();
my_attacks.remove_attacks(*this,move.StartSquare(),side);
Square sq;
i = 0;
j = -1;
while (i < 16 && ((sq = PiecePos(side,i)) != move.StartSquare()))
{
if (sq == Square::Invalid() && j==-1)
j = i;
i++;
}
assert (i < 16);
// keep pieces near the start of the PiecePos array, if possible:
if (j == -1)
my_PiecePos[i][side] = move.DestSquare();
else
{
my_PiecePos[j][side] = move.DestSquare();
my_PiecePos[i][side] = Square::Invalid();
}
if (!move.Capture().IsEmpty())
my_attacks.remove_attacks(*this,target,oside);
if (move.Special() == ExtendedMove::Promotion)
my_Contents[move.DestSquare()] = Piece(move.PromoteTo(),side);
else
my_Contents[move.DestSquare()] = move.PieceMoved();
my_Contents[move.StartSquare()] = Piece::EmptyPiece();
my_EnPassantSq[side] = Square::Invalid();
if (move.PieceMoved().Type() == Piece::King)
{
my_KingPos[side] = move.DestSquare();
if ((CastleStatus(side) != CastledQSide) &&
(CastleStatus(side) != CastledKSide))
my_CastleStatus[side] = CantCastleEitherSide;
}
else if (move.PieceMoved().Type() == Piece::Rook)
{
my_RFileCount[move.StartSquare().File()-1][side]--;
my_RFileCount[move.DestSquare().File()-1][side]++;
UpdateCastleStatus(side,move.StartSquare());
}
else if (move.PieceMoved().Type() == Piece::Pawn)
{
if (Util::Abs(move.StartSquare().Rank(side) -
move.DestSquare().Rank(side)) == 2)
{
my_EnPassantSq[side] = move.DestSquare();
}
if (move.Special() == ExtendedMove::Promotion)
{
my_Material[side].remove_piece(Piece::Pawn);
my_Material[side].add_piece(move.PromoteTo());
if (move.PromoteTo() == Piece::Rook)
{
my_RFileCount[move.StartSquare().File()-1][side]--;
my_RFileCount[move.DestSquare().File()-1][side]++;
}
my_PFileCount[move.StartSquare().File()-1][side]--;
}
}
if (!move.Capture().IsEmpty())
{
my_Material[oside].remove_piece(move.Capture().Type());
i = 0;
while (i < 16 && PiecePos(oside,i) != target)
i++;
//assert(i < 16);
if (i>=16)
i = 0; // break here
//if (move.Special() == ExtendedMove::EnPassant)
// my_Contents[target] = Piece::EmptyPiece();
my_PiecePos[i][oside] = Square::Invalid();
if (move.Capture().Type() == Piece::Pawn)
{
my_PFileCount[target.File()-1][oside]--;
}
else if (move.Capture().Type() == Piece::Rook)
{
my_RFileCount[move.DestSquare().File()-1][oside]--;
UpdateCastleStatus(oside,move.DestSquare());
}
if (my_Contents[move.DestSquare()].Type() == Piece::Pawn &&
move.Special() != ExtendedMove::Promotion)
{
my_PFileCount[move.StartSquare().File()-1][side]--;
my_PFileCount[move.DestSquare().File()-1][side]++;
}
}
my_attacks.add_attacks(*this,move.DestSquare(),side);
if (move.Special() == ExtendedMove::EnPassant)
{
my_attacks.add_discovered_attacks(*this,
move.StartSquare(),move.DestSquare());
my_attacks.remove_discovered_attacks(*this,
move.DestSquare(),move.StartSquare());
my_Contents[target] = Piece::EmptyPiece();
my_attacks.add_discovered_attacks(*this,
target,Square::Invalid());
}
else
{
my_attacks.add_discovered_attacks(*this,
move.StartSquare(),move.DestSquare());
if (move.Capture().IsEmpty())
my_attacks.remove_discovered_attacks(*this,
move.DestSquare(),move.StartSquare());
}
}
my_EnPassantSq[oside] = Square::Invalid();
my_Side = OppositeSide();
#ifdef DEBUG_ATTACKS
{
Attacks new_attacks;
new_attacks.compute_attacks(*this,White);
new_attacks.compute_attacks(*this,Black);
assert(new_attacks == my_attacks);
}
#endif
}
void Board::undo_castling(const Square &kp,
const Square &oldkingsq, const Square &newrooksq,
const Square &oldrooksq)
{
my_attacks.remove_attacks(*this,kp,my_Side);
my_attacks.remove_attacks(*this,newrooksq,my_Side);
my_Contents[kp] = Piece::EmptyPiece();
my_Contents[oldrooksq] = Piece(Piece::Rook,my_Side);
my_Contents[newrooksq] = Piece::EmptyPiece();
my_Contents[oldkingsq] = Piece(Piece::King,my_Side);
my_KingPos[my_Side] = oldkingsq;
my_RFileCount[oldrooksq.File()-1][my_Side]++;
my_RFileCount[newrooksq.File()-1][my_Side]--;
int i = 0;
Boolean fixed_rook = False;
Boolean fixed_king = False;
while (i<16)
{
Square sq(PiecePos(my_Side,i));
if (sq == newrooksq)
{
my_PiecePos[i][my_Side] = Square::Invalid();
if (fixed_rook && fixed_king) i++;
}
else if (sq == kp)
{
my_PiecePos[i][my_Side] = Square::Invalid();
if (fixed_rook && fixed_king) i++;
}
else if (sq == Square::Invalid() && !fixed_rook)
{
my_PiecePos[i][my_Side] = oldrooksq;
fixed_rook = True;
i++;
}
else if (sq == Square::Invalid() && !fixed_king)
{
my_PiecePos[i][my_Side] = oldkingsq;
fixed_king = True;
i++;
}
else
i++;
}
my_attacks.add_attacks(*this,oldkingsq,my_Side);
my_attacks.add_attacks(*this,oldrooksq,my_Side);
my_attacks.remove_discovered_attacks(*this,oldkingsq,newrooksq);
}
void Board::UndoMove( const ReversibleMove &rmove )
{
my_Side = OppositeColor(my_Side);
if (!rmove.IsNull())
{
const ColorType opp_side = OppositeColor(my_Side);
int i;
if (rmove.Special() == ExtendedMove::KCastle)
{
Square kp = KingPos(my_Side);
Square oldrooksq(kp+1);
Square newrooksq(kp-1);
Square oldkingsq(kp-2);
undo_castling(kp,oldkingsq,newrooksq,oldrooksq);
}
else if (rmove.Special() == ExtendedMove::QCastle)
{
Square kp = KingPos(my_Side);
Square oldrooksq(kp-2);
Square newrooksq(kp+1);
Square oldkingsq(kp+2);
undo_castling(kp,oldkingsq,newrooksq,oldrooksq);
}
else
{
// not castling
my_attacks.remove_attacks(*this,rmove.DestSquare(),my_Side);
Square target;
if (!rmove.Capture().IsEmpty())
{
if (rmove.Special() == ExtendedMove::EnPassant)
target = (my_Side == White) ? rmove.DestSquare() + RankIncr :
rmove.DestSquare() - RankIncr;
else
target = rmove.DestSquare();
}
// fix up start square:
if (rmove.Special() == ExtendedMove::Promotion)
{
my_Contents[rmove.StartSquare()] = Piece(Piece::Pawn,my_Side);
my_PFileCount[rmove.StartSquare().File()-1][my_Side]++;
}
else
{
if (rmove.Special() != ExtendedMove::EnPassant)
my_Contents[rmove.StartSquare()] = rmove.PieceMoved();
switch (rmove.PieceMoved().Type())
{
case Piece::Pawn:
my_PFileCount[rmove.StartSquare().File()-1][my_Side]++;
my_PFileCount[rmove.DestSquare().File()-1][my_Side]--;
break;
case Piece::Rook:
my_RFileCount[rmove.StartSquare().File()-1][my_Side]++;
my_RFileCount[rmove.DestSquare().File()-1][my_Side]--;
break;
case Piece::King:
my_KingPos[my_Side] = rmove.StartSquare();
break;
default:
break;
}
}
Boolean start_fix_up = False;
i = 0; int j = 0;
while (i<16)
{
if ( !start_fix_up && PiecePos(my_Side,i) == Square::Invalid() )
{
my_PiecePos[i][my_Side] = rmove.StartSquare();
start_fix_up = True;
i++;
}
else if (PiecePos(my_Side,i) == rmove.DestSquare())
{
my_PiecePos[i][my_Side] = Square::Invalid();
if (start_fix_up) i++;
j++;
}
else
i++;
}
assert(j>0);
// fix up dest square
if (rmove.Special() == ExtendedMove::EnPassant)
{
my_Contents[rmove.DestSquare()] = Piece::EmptyPiece();
my_Contents[target] = Piece(Piece::Pawn,opp_side);
my_PFileCount[rmove.DestSquare().File()-1][opp_side]++;
my_Material[opp_side].add_piece(Piece::Pawn);
for (i = 0; i < 16; i++)
{
if( PiecePos(opp_side,i) == Square::Invalid())
{
my_PiecePos[i][opp_side] = target;
break;
}
}
// It tricky fixing up the attacks. Pretend we
// made two moves: a capture "sideways" then a
// pawn move forward, and undo them in reverse order.
my_attacks.add_attacks(*this,target,opp_side);
my_attacks.add_discovered_attacks(*this,
rmove.DestSquare(),target);
my_attacks.remove_discovered_attacks(
*this,target,rmove.DestSquare());
my_Contents[rmove.StartSquare()] = rmove.PieceMoved();
my_attacks.add_attacks(*this,rmove.StartSquare(),my_Side);
my_attacks.remove_discovered_attacks(
*this,rmove.StartSquare(),target);
}
else
{
my_Contents[rmove.DestSquare()] = rmove.Capture();
my_EnPassantSq[opp_side] = Square::Invalid();
if (rmove.Special() == ExtendedMove::Promotion)
{
my_Material[my_Side].add_piece(Piece::Pawn);
my_Material[my_Side].remove_piece(rmove.PromoteTo());
}
if (!rmove.Capture().IsEmpty())
{
my_Material[opp_side].add_piece(rmove.Capture().Type());
for (i = 0; i < 16; i++)
{
if( PiecePos(opp_side,i) == Square::Invalid())
{
my_PiecePos[i][opp_side] = rmove.DestSquare();
break;
}
}
if (rmove.Capture().Type() == Piece::Rook)
{
my_RFileCount[rmove.DestSquare().File()-1][opp_side]++;
}
else if (rmove.Capture().Type() == Piece::Pawn)
{
my_PFileCount[rmove.DestSquare().File()-1][opp_side]++;
}
my_attacks.add_attacks(*this,rmove.StartSquare(),my_Side);
my_attacks.add_attacks(*this,target,OppositeColor(my_Side));
my_attacks.remove_discovered_attacks(
*this,rmove.StartSquare(),rmove.DestSquare());
}
else
{
// not a capture move
my_attacks.add_attacks(*this,rmove.StartSquare(),my_Side);
my_attacks.add_discovered_attacks(*this,
rmove.DestSquare(),rmove.StartSquare());
my_attacks.remove_discovered_attacks(
*this,rmove.StartSquare(),rmove.DestSquare());
}
}
}
my_EnPassantSq[my_Side] = rmove.Old_EnPassantSq(my_Side);
my_EnPassantSq[OppositeColor(my_Side)] = rmove.Old_EnPassantSq(
OppositeColor(my_Side));
my_CastleStatus[my_Side] = rmove.Old_CastleStatus(my_Side);
my_CastleStatus[OppositeColor(my_Side)] =
rmove.Old_CastleStatus(OppositeColor(my_Side));
}
my_HashCode = rmove.Old_HashCode();
#ifdef DEBUG_ATTACKS
{
Attacks new_attacks;
new_attacks.compute_attacks(*this,White);
new_attacks.compute_attacks(*this,Black);
assert (new_attacks == my_attacks);
}
#endif
}
Board::CheckStatusType Board::CheckStatus() const
{
if (num_attacks(KingPos(Side()),OppositeSide()) > 0)
return InCheck;
else
return NotInCheck;
}
static void set_bad( istream &i )
{
i.clear( ios::badbit | i.rdstate() );
}
istream & operator >> (istream &i, Board &board)
{
// read in a board position in Forsythe-Edwards (FEN) notation.
static char buf[128];
for (int j=0;j<BoardSize;j++)
{
board.my_Contents[j] = Piece::EmptyPiece();
}
i.getline(buf,128);
if (!i)
return i;
Square sq;
char *bp = buf;
char c;
while (isspace(*bp)) bp++;
for (int line = 0; line < 8; line++)
{
int sqval = line*RankIncr;
while ((c = *bp) != ' ' && c != '/')
{
Piece piece;
if (isdigit(c))
{
sqval += (*bp - '0');
bp++;
}
else if (isalnum(c))
{
if (!Square(sqval).OnBoard())
{
set_bad(i);
return i;
}
switch(c)
{
case 'p': piece = Piece(Piece::Pawn,Black);
break;
case 'n': piece = Piece(Piece::Knight,Black);
break;
case 'b': piece = Piece(Piece::Bishop,Black);
break;
case 'r': piece = Piece(Piece::Rook,Black);
break;
case 'q': piece = Piece(Piece::Queen,Black);
break;
case 'k': piece = Piece(Piece::King,Black);
break;
case 'P': piece = Piece(Piece::Pawn,White);
break;
case 'N': piece = Piece(Piece::Knight,White);
break;
case 'B': piece = Piece(Piece::Bishop,White);
break;
case 'R': piece = Piece(Piece::Rook,White);
break;
case 'Q': piece = Piece(Piece::Queen,White);
break;
case 'K': piece = Piece(Piece::King,White);
break;
default:
set_bad(i);
return i;
}
board.my_Contents[sqval] = piece;
sqval++;
bp++;
}
else // not a letter or a digit
{
set_bad(i);
return i;
}
}
if (c=='/') ++bp; // skip delimiter
}
while (isspace(*bp)) bp++;
ColorType side;
if (toupper(*bp) == 'W')
board.my_Side = White;
else if (toupper(*bp) == 'B')
board.my_Side = Black;
else
{
set_bad(i);
return i;
}
bp++;
while (isspace(*bp)) bp++;
c = *bp;
if (c == '-')
{
board.my_CastleStatus[White] =
board.my_CastleStatus[Black] = Board::CantCastleEitherSide;
bp++;
}
else
{
int k = 0;
for (; !isspace(*bp); bp++)
{
if (*bp == 'K')
k += 1;
else if (*bp == 'Q')
k += 2;
else if (*bp == 'k')
k += 4;
else if (*bp == 'q')
k += 8;
else
{
set_bad(i);
return i;
}
}
static Board::CastleType vals[4] =
{ Board::CantCastleEitherSide, Board::CanCastleKSide,
Board::CanCastleQSide, Board::CanCastleEitherSide };
board.my_CastleStatus[White] = vals[k % 4];
board.my_CastleStatus[Black] = vals[k / 4];
}
while (isspace(*bp)) bp++;
c = *bp;
if (c == '-')
{
board.my_EnPassantSq[White].SetInvalid();
board.my_EnPassantSq[Black].SetInvalid();
}
else if (isalpha(c))
{
char sqbuf[2];
sqbuf[0] = *bp++;
sqbuf[1] = *bp++;
Square epsq = Square::Value(sqbuf);
if (epsq.IsInvalid())
{
set_bad(i);
return i;
}
board.my_EnPassantSq[board.Side()].SetInvalid();
board.my_EnPassantSq[board.OppositeSide()] =
Square::Value(sqbuf) - (RankIncr * Direction[board.Side()]);
}
else
{
set_bad(i);
return i;
}
board.set_secondary_vars();
return i;
}
ostream & operator << (ostream &o, Board &board)
{
// write out the board in Forsythe-Edwards (FEN) notation.
for (int i=1;i<=8;i++)
{
int j = 1;
while (j <= 8)
{
int n = 0;
Square sq;
Piece p;
do
{
sq = Square(j,i,Black);
p = board.my_Contents[sq];
if (!p.IsEmpty())
break;
++j; ++n;
} while (j <= 8);
if (n)
o << (char)(n + '0');
if (!p.IsEmpty())
{
char img = Piece::Image(p.Type());
if (p.Color() == Black) img = tolower(img);
o << img;
j++;
}
}
if (i != 8) o << '/';
}
if (board.Side() == White)
o << " w";
else
o << " b";
// used in I/O of castling data:
const Boolean kcastle[6] = { True, True, False, False, False, False };
const Boolean qcastle[6] = { True, False, True, False, False, False };
// note : unfortunately FEN doesn't allow recording if castling
// has taken place, only whether or not it is possible.
Board::CastleType wcs = board.CastleStatus(White);
Board::CastleType bcs = board.CastleStatus(Black);
o << ' ';
if (!kcastle[(int)wcs] && !qcastle[(int)bcs])
o << '-';
else
{
if (kcastle[(int)wcs])
o << 'K';
if (qcastle[(int)wcs])
o << 'Q';
if (kcastle[(int)bcs])
o << 'k';
if (qcastle[(int)bcs])
o << 'q';
}
o << ' ';
Square epsq(board.EnPassantSq(board.OppositeSide()));
if (epsq.IsInvalid())
o << '-';
else
{
// FEN stores the destination square for an en passant capture;
// we store the location of the capturable pawn.
Square target(epsq + (RankIncr * Direction[board.Side()]));
o << target.FileImage() << target.RankImage();
}
// FEN is supposed to include the halfmove and fullmove numbers,
// but these are attributes of the game - they are not stored in
// the board.
o << " 0 1";
o << endl;
return o;
}